package structure.interfaces;

import java.util.ArrayList;
import utils.Utils;

/**
 * abstract class which represent unary operator nodes 
 * 
 */
public abstract class IOperatorUnary implements INode {
	
	protected INode child;  //unary operator have one child
	
	// return child
	public INode getChild() {
		return child;
	}
	
	// set chid
	public void setChild(INode child) {
		this.child = child;
	}
	
	/**
	 * generating random sub tree which is no deeper than the given deep
	 * @param deep - max deep of the sub tree
	 */
	public void generateSubTree(int deep) {
		if (deep > 1) {
			child = Utils.getRandomOperator();
			child.generateSubTree(deep-1);	
		}
		else {
			child = Utils.getRandomNullaryOperator();
		}
	}
	
	/**
	 * @return the tree max height
	 */
	public int getDeep() {
		return 1 + child.getDeep();
	}
	
	/**
	 * @return array of the chromosome childrens (null if there is no children)
	 */
	public INode[] getChildrens() {
		return new INode[]{child};
	}
	
	@Override
	public String toString() {
		return this.getDescription();
	}

	/**
	 * @return cloned tree
	 */
	public INode clone() {
		IOperatorUnary operator = null;
		Class<? extends IOperatorUnary> c = this.getClass();
		try {
			operator = c.newInstance();
		} catch (Throwable t) {
			t.printStackTrace();
		}
		operator.setChild(child.clone());
		return operator;
	}
	
	/** 
	 * @param crossNode - the cross node in the current tree
	 * @param newSubTree - the new sub tree  
	 * @return cloned tree composed from the original tree and the newSubTree (which replace the crossNode subtree)
	 */
	public INode clone(INode crossNode, INode newSubTree) {
		if (this == crossNode) {
			return newSubTree.clone();
		}
		else {
			IOperatorUnary operator = null;
			Class<? extends IOperatorUnary> c = this.getClass();
			try {
				operator = c.newInstance();
			} catch (Throwable t) {
				t.printStackTrace();
			}
			operator.setChild(child.clone(crossNode, newSubTree));
			return operator;
		}
	}

	/**
	 * @return array list holding all the nodes in the tree
	 */	
	public ArrayList<INode> getAllChildrens() {
		ArrayList<INode> nodes = new ArrayList<INode>();
		nodes.add(this);
		nodes.addAll(child.getAllChildrens());
		return nodes;
	}
	

	/**
	 * Get random node
	 * @return the selected random node
	 */
	 @Override
	 public INode getRandomNode() {
	  if (utils.Utils.rand.nextDouble() < 0.5)
	   return child.getRandomNode();
	  else 
	   return this;
	 }
}
